//    Copyright (C) Kherty.  All rights reserved.
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using OpenLS.Core.Serialization;
using OpenLS.Spreadsheet.Expressions;

namespace OpenLS.Spreadsheet
{
    ///<summary>
    /// Represents a conditional formatting on a group of cells in a worksheet.
    ///</summary>
    public class ConditionalFormatting: IOfficeXmlSerializable
    {
        #region IOfficeXmlSerializable Members

        void IOfficeXmlSerializable.ReadXml(ReadContext context)
        {
            using (ReadContext c = context.Read("conditionalFormatting"))
            {
                Pivot = c.GetOptionalBoolean("pivot", false);
                var sqref = c.GetOptionalString("sqref");
                if (sqref != null)
                {
                    var worksheet = c.GetData<Worksheet>();
                    Reference = worksheet.GetRangeReference(sqref);
                }
                while (c.ReaderLocalName == "cfRule")
                    Rules.Add(ConditionalFormattingRule.ReadRule(c, this));
                Extension = c.ReadOptionalElement<ExtensionElement>("extLst");
                if (sqref == null)
                {
                    throw new IgnoreElementException();
                }
            }
        }

        void IOfficeXmlSerializable.WriteXml(WriteContext context)
        {
            using (WriteContext c = context.Write("conditionalFormatting"))
            {
                c.SetData(typeof (ConditionalFormatting), this);
                c.SetOptionalBoolean("pivot", _pivot, false);
                if (Range != null)
                {
                    var sqref = Reference.Label;
                    c.SetOptionalString("sqref", sqref);
                }
                foreach (ConditionalFormattingRule r in Rules)
                    r.PrepareBeforeWrite(this, ConditionalFormattingRule.FormatType.Default);

                foreach (ConditionalFormattingRule r in Rules)
                    c.WriteElement(r);
                c.WriteOptionalElement(Extension);
            }
        }

        ///<summary>
        /// Get or set an application specific extension element. This element will be saved to file
        ///</summary>
        public ExtensionElement Extension { get; set; }

        private ObservableCollection<ConditionalFormattingRule> _rules;
        ///<summary>
        /// Get the collection of conditional formatting rules.
        ///</summary>
        public ObservableCollection<ConditionalFormattingRule> Rules
        {
            get
            {
                if (_rules == null)
                {
                    _rules = new ObservableCollection<ConditionalFormattingRule>();
                    _rules.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(_rules_CollectionChanged);
                }
                return _rules;
            }
        }

        void _rules_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
                foreach (ConditionalFormattingRule rule in e.OldItems)
                    rule.Parent = null;
            if (e.NewItems != null)
                foreach (ConditionalFormattingRule rule in e.NewItems)
                    rule.Parent = this;
        }
        internal enum ApplyReason
        {
            ValueChanged,
            Added
        }
        internal void Apply(ApplyReason reason)
        {
            foreach (ConditionalFormattingRule rule in Rules)
            {
                
                    rule.ApplyRange(Reference.Range);
            }
            foreach (Cell cell in Reference.Range.GetNonEmptyCells())
            {
                if (reason == ApplyReason.Added)
                    cell.AddConditionalFormatting (this);
                foreach (ConditionalFormattingRule rule in Rules)
                {
                    bool applied = rule.Apply(cell);
                    if (applied && rule.StopIfTrue)
                        break;
                }
            }
        }
        internal void UnApply()
        {
            foreach (Cell cell in Reference.Range.GetNonEmptyCells())
            {
                cell.ResetConditionalFormatting(this);
            }
        }
	
        #endregion
        private bool _pivot;

        ///<summary>
        /// Get or set whether the conditional formatting applies to a pivot table. This is false by default.
        ///</summary>
        public bool Pivot
        {
            get { return _pivot; }
            set { _pivot = value; }
        }

        ///<summary>
        /// Get or set the range where the conditional formatting should be applied.
        ///</summary>
        public Range Range
        {
            get
            {
                return Reference.Range;
            }
            set
            {
                Reference = value.ToReference();
            }
        }

        private void hookEvents()
        {
            if (Reference != null)
            {
                //Reference.Worksheet.AddWeakCellAdded(this.Worksheet_CellAdded);
                Reference.Worksheet.CellAdded += worksheetCellAdded;
                Reference.Worksheet.CellMoved += worksheetCellMoved;
            }
        }

        void worksheetCellMoved(object sender, CellMovedArgs e)
        {
            Range range = Range;
            bool containsOrigin = range.Contains(e.Origin);
            bool containsCurrent = range.Contains(e.Cell);
            if (containsOrigin == containsCurrent)
                return;
            if (containsOrigin)
                e.Cell.ResetConditionalFormatting(this);
            if (containsCurrent)
                e.Cell.AddConditionalFormatting (this);
        }

        private void unhookEvents()
        {
            if (Reference != null)
            {
                Reference.Worksheet.CellAdded -= worksheetCellAdded;
                Reference.Worksheet.CellMoved -= worksheetCellMoved;
            }
        }

        void worksheetCellAdded(object sender, CellEventArgs e)
        {
            if (!Range.Contains(e.Cell))
                return;
            Apply(ApplyReason.Added);
        }

        private RangeReference _reference;
        private RangeReference Reference
        {
            get { return _reference; }
            set
            {
                unhookEvents();
                _reference = value;
                hookEvents();
            }
        }

        internal Worksheet Worksheet
        {
            get { return Reference.Worksheet; }
        }

        internal Cell ReferenceCell
        {
            get { return this.Range.TopLeftCell; }
        }
    }
}
